#pragma once
#include "Vertex.h"

class Graph{
public:
	Graph(){
		gsize = 0;
	}


	void add_vertex(){ // добавление вершины
		gsize += 1;
		Vertices.resize(gsize);
		colors.resize(gsize);
		Vertices[gsize - 1].set_id(gsize - 1);
	}


	void set_size(int nsize){ // задание размера графа (кол-во вершн)
		if (nsize < gsize){
			printf("Some important vertices may be lost. Can cause a crash.");
			Vertices.resize(nsize);
			gsize = nsize;
		}
		else{
			for (int i = gsize; i != nsize; i++)
				add_vertex();
			gsize = nsize;
		}
	}

	Vertex get_vertex(int n){ // получение ребра
		return Vertices[n];
	}

	void add_edge(int from, int to, int weigh, bool color = false){ // добавление ребра
		Vertices[from].add_edge(std::make_shared<Vertex>(Vertices[to]), weigh, color);
	}
	void remove_edge(int from, int to){ // удаление ребра
		Vertices[from].remove_edge(to);
	}

	void remove_vertex(int rmvert){ // удаление вершины
		for (auto it = Vertices.begin(); it != Vertices.end(); it++)
			(*it).remove_edge(rmvert);
	}

	void invert(){ // транспонирование
		for (int i = 0; i < gsize; i++){
			std::vector<std::pair<int, int>> task = Vertices[i].invert();
			for (unsigned int j = 0; j < task.size(); j++)
				add_edge(task[j].first, i, task[j].second, true);
		}
		for (int i = 0; i < gsize; i++)
			Vertices[i].restore_edges_color();
	}

	void dfs_it(std::shared_ptr<Vertex> v){ // DFS
		std::vector<e> edges = (*v).get_edges();
		for (unsigned int i = 0; i < edges.size(); i++)
		if (colors[(*(edges[i].next)).get_id()] == 0){ // если непосещённая вершина
			ptrs_in.push_back(superVertex.get_edges()[(*(edges[i].next)).get_id()].next);
			colors[(*(edges[i].next)).get_id()] = 1; // помечаем "активной"
			dfs_it(superVertex.get_edges()[(*(edges[i].next)).get_id()].next); // запускаемся снова от нашей вершины
			colors[(*(edges[i].next)).get_id()] = 2; // помечаем "обработанной"
			ptrs_out.push_back(superVertex.get_edges()[(*(edges[i].next)).get_id()].next);
		}
	}

	void reiterate(){ // вершины расставляются в порядке входа и выхода
		superVertex.restore();
		for (int i = 0; i < gsize; i++)
			superVertex.add_edge(std::make_shared<Vertex>(Vertices[i]), 0);
		ptrs_in.clear();
		ptrs_out.clear();
		for (int i = 0; i < gsize; i++)
			colors[i] = 0;
		dfs_it(std::make_shared<Vertex>(superVertex));
		for (int i = 0; i < gsize; i++)
			colors[i] = 0;
	}

	struct iterator_in{ //итератор входа
		std::vector<std::shared_ptr<Vertex>>::iterator  ptr;
		iterator_in(std::vector<std::shared_ptr<Vertex>>::iterator ptr_) : ptr(ptr_){}
		Vertex operator*() { return *(*ptr); }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator->() { return ptr; }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator++() { return ++ptr; }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator--() { return --ptr; }
		bool operator==(const iterator_in other) const { return ptr == other.ptr; }
		bool operator!=(const iterator_in other) const { return !(*this == other); }
	};

	size_t size() const { return gsize; }

	iterator_in begin_i() { return ptrs_in.begin(); }
	iterator_in end_i() { return ptrs_in.begin() + gsize; }

	struct iterator_out{ // итератор выхода
		std::vector<std::shared_ptr<Vertex>>::iterator  ptr;
		iterator_out(std::vector<std::shared_ptr<Vertex>>::iterator  ptr_) : ptr(ptr_){}
		Vertex operator*() { return *(*ptr); }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator->() { return ptr; }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator++() { return ++ptr; }
		std::vector<std::shared_ptr<Vertex>>::iterator  operator--() { return --ptr; }
		bool operator==(const iterator_out other) const { return ptr == other.ptr; }
		bool operator!=(const iterator_out other) const { return !(*this == other); }
	};

	iterator_out begin_o() { return ptrs_out.begin(); }
	iterator_out end_o() { return ptrs_out.begin() + gsize; }

	std::vector<std::shared_ptr<Vertex>> get_dfs_in_order(){ // порядок входа при DFS
		return ptrs_in;
	}

	std::vector<std::shared_ptr<Vertex>> get_dfs_out_order(){ // порядок выхода при DFS
		return ptrs_out;
	}

	int checkcolor(int n){ // цвет вершины
		return colors[n];
	}

	void setcolor(int pos, int color){ // покраска вершины
		colors[pos] = color;
	}

private:
	std::vector<std::shared_ptr<Vertex>> ptrs_in; // порядок входа в вершины
	std::vector<std::shared_ptr<Vertex>> ptrs_out; // порядок выхода
	std::vector<int> colors; // цвета вершин
	Vertex superVertex;
	std::vector<Vertex> Vertices; // вершины
	int gsize; // размер графа
};